﻿using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;

namespace GBricks.Collections
{
  partial class Comparers
  {
    public static IEqualityComparer<IEnumerable<T>> OrderedCollection<T>(IEqualityComparer<T> itemComparer = null) {
      Contract.Ensures(Contract.Result<IEqualityComparer<IEnumerable<T>>>() != null);
      return OrderedCollection<IEnumerable<T>, T>(itemComparer);
    }

    public static IEqualityComparer<TCollection> OrderedCollection<TCollection, T>(IEqualityComparer<T> itemComparer = null) where TCollection : IEnumerable<T> {
      Contract.Ensures(Contract.Result<IEqualityComparer<TCollection>>() != null);
      return new OrderedCollectionEqualityComparer<TCollection, T>(itemComparer);
    }

    [Serializable]
    private sealed class OrderedCollectionEqualityComparer<TCollection, T> : EqualityComparer<TCollection> where TCollection : IEnumerable<T>
    {
      public OrderedCollectionEqualityComparer(IEqualityComparer<T> comparer = null) {
        ItemComparer = comparer ?? EqualityComparer<T>.Default;
      }

      private IEqualityComparer<T> ItemComparer { get; set; }

      [ContractInvariantMethod]
      private void Invariant() {
        Contract.Invariant(ItemComparer != null);
      }

      public override bool Equals(TCollection x, TCollection y) {
        if(x == null) {
          return y == null;
        } else if(y == null) {
          return false;
        }//if

        var xc = x as ICollection<T>;
        if(xc != null) {
          var yc = y as ICollection<T>;
          if(yc != null && xc.Count != yc.Count) {
            return false;
          }//if
        }//if

        return Enumerable.SequenceEqual(x, y, ItemComparer);
      }

      public override int GetHashCode(TCollection obj) {
        return HashCode.CalculateAll(obj, ItemComparer);
      }

      public override bool Equals(object obj) {
        var other = obj as OrderedCollectionEqualityComparer<TCollection, T>;
        return other != null && Equals(other.ItemComparer, ItemComparer);
      }

      public override int GetHashCode() {
        return ItemComparer.GetHashCode();
      }
    }
  }
}
